package com.lennon.eoi.executors.query;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.lennon.eoi.constants.EOIExeclConstants;

/**
 * 批量查询
 * 
 * @author lennon
 * @time 2017年8月27日下午7:38:02
 *
 * @param <T>
 * @param <E>
 */
public abstract class AbstractBatchQueryList<T, E> implements EOIQueryList<T, E> {
    
	// 单次查询最大值
	protected Integer queryMaxCount;
	
	// 公有线程池
	private ExecutorService executorService = Executors.newFixedThreadPool(2);;
	
    // 日志打印器
	private Logger logger = LoggerFactory.getLogger(AbstractBatchQueryList.class);
    
    // 默认将单次查询的最大值限定为500
    protected AbstractBatchQueryList() {
    	this.queryMaxCount =  EOIExeclConstants.EXECL_QUERY_MAX_COUNT;
    }
    
    // 通过传参数指定查询的最大值
    protected AbstractBatchQueryList(Integer queryMaxCount) {
    	if(queryMaxCount == null || queryMaxCount <= 0) {
    		this.queryMaxCount =  EOIExeclConstants.EXECL_QUERY_MAX_COUNT;
    		return ;
    	}
    	this.queryMaxCount = queryMaxCount;
    }
    
    /**
     * 将大量查询通过分批次的方式来实现，达到大任务转为小任务查询的目的
     * 
     * @param queryParamObject queryVO
     */
    @Override
    public List<E> queryList(T queryParamObject) {
        
        Long count = this.queryCount(queryParamObject);
        List<CompletableFuture<List<E>>> futures = new ArrayList<CompletableFuture<List<E>>>();
        
        // 查无数据
        if(count == null || count <= 0) {
        	return new ArrayList<E>();
        }
            
        // 如果查询结果小于单次查询的最大值，则直接查询并返回
        if(count < queryMaxCount) {
            return queryList(queryParamObject, 0, count.intValue());
        }
        
        // 查询结果
        List<E> lstResult = new ArrayList<E>();
        
        // 通过任务线程池来分批查询
		CompletionService<List<E>> completionService = new ExecutorCompletionService<List<E>>(executorService);

		// 将任务分批
		for (int i = 0; i < count / queryMaxCount ; i++) {
			final int firstIndex = i * queryMaxCount;
			final int endIndex = (firstIndex + queryMaxCount < count.intValue())
					? i + queryMaxCount
					: count.intValue();
			
			// 异步查询任务
			completionService.submit(new Callable<List<E>>() {
				
				@Override
				public List<E> call() throws Exception {
					return queryList(queryParamObject, firstIndex, endIndex);
				}
				
			});
		}

		// 循环从队列中将任务处理结果取出来
        try {
        	for (int i = 0; i < count / queryMaxCount; i++) {
        		// completionService.take() 内部实现是阻塞线程，所以这里如果获取不到会造成阻塞。所以采用poll超时来判断如果超时则取消任务
        		 Future<List<E>> future = completionService.poll(3, TimeUnit.SECONDS);
        		 lstResult.addAll(future.get(3, TimeUnit.SECONDS));
        	}
		} catch (InterruptedException e) {
			logger.error("分批读取数据异常中断：" , e);
		} catch (ExecutionException e) {
			logger.error("分批读取数据异常中断：" , e);
		} catch (TimeoutException e) {
			logger.error("分批读取数据异常超时：" , e);
		}
        return lstResult;
        
    }

    // 分页查询器
    protected abstract List<E> queryList(T queryParamObject, Integer firstPage, Integer endPage);
}
